package com.googlecode.ptrace;

import java.io.*;

public class Ptrace {
	protected OutputStream stream = null;

	protected static OutputStream staticStream = null;

	private int stackDepth = 0;

	private String message = null;

	private boolean showStackDepth = false;

	private boolean showCompleteClassName = false;

	public void showStackDepth(boolean show) {
		showStackDepth = show;
	}

	public void showCompleteClassName(boolean show) {
		showCompleteClassName = show;
	}

	public Ptrace(String message) {
		Throwable t = new Throwable();
		StackTraceElement[] elements = t.getStackTrace();
		stackDepth = elements.length - 1;
		StackTraceElement topElement = elements[1];
		String info = getTraceInfo(topElement, false);
		this.message = mergeMessage(info, message);
	}

	public static String getTraceInfo(StackTraceElement element, boolean showCompleteClassName) {
		StringBuffer buffer = new StringBuffer();

		String className = element.getClassName();
		/*
		 * if(showStackDepth){ buffer.append(stackDepth); }
		 */

		if (!showCompleteClassName) {
			int dot = className.lastIndexOf('.');
			className = className.substring(dot + 1, className.length());
		}

		buffer.append("[");
		buffer.append(className);
		buffer.append("|");
		buffer.append(element.getMethodName());
		buffer.append(":");
		buffer.append(element.getLineNumber());
		buffer.append("]");

		return buffer.toString();
	}

	public static String mergeMessage(String info, String message) {
		return info + " " + message;
	}

	protected static boolean displayable = true;

	public String toString() {
		return message;
	}

	public void print() {
		System.out.println(message);
	}

	public void print(OutputStream out) {
		try {
			out.write(this.message.getBytes());
		}
		catch (IOException oops) {
			oops.printStackTrace();
		}
	}

	public Ptrace(OutputStream stream) {
		this.stream = stream;
	}

	public void setOutput(OutputStream stream) {
		staticStream = stream;
	}

	public static void ptrace(Object obj) {
		ptrace(obj.toString(), 0, 2);
	}

	public static void ptrace(String message) {
		ptrace(message, 0, 2);
	}

	public static void ptrace(String... messages) {
		StringBuffer resultedMessage = new StringBuffer();

		for (String message : messages) {
			resultedMessage.append(message).append(", ");
		}
		ptrace(resultedMessage.toString(), 0, 2);
	}

	protected static boolean showAll = false;
	
	public static void ptrace(String message, int severity, int level) {
		// Properties properties =
		// PropertiesLoader.loadProperties("ptrace.properties");
		// displayable = Boolean.valueOf((String)
		// properties.get("displayable"));
		if (severity < defaultServerity && !showAll) {
			return;
		}
		String msg = message(message, level + 1);
		if (staticStream == null) {
			System.out.println(msg);
		}
		else {
			try {
				staticStream.write(msg.getBytes());
			}
			catch (IOException ex) {
				ex.printStackTrace();
			}
		}
	}

	public static String message(String message) {
		return message(message, 2);
	}

	public static String message(String message, int level) {
		StringBuffer buffer = new StringBuffer();
		Throwable t = new Throwable();
		StackTraceElement[] elements = t.getStackTrace();
		// stackDepth = elements.length - 1;
		StackTraceElement topElement = elements[level];
		String info = getTraceInfo(topElement, false);
		buffer.append(info).append(message);
		return buffer.toString();
	}

	/**
	 * @return the showCompleteClassName
	 */
	public boolean isShowCompleteClassName() {
		return showCompleteClassName;
	}

	/**
	 * @param showCompleteClassName the showCompleteClassName to set
	 */
	public void setShowCompleteClassName(boolean showCompleteClassName) {
		this.showCompleteClassName = showCompleteClassName;
	}

	/**
	 * @return the showStackDepth
	 */
	public boolean isShowStackDepth() {
		return showStackDepth;
	}

	public static final int DEBUG_SERVERITY = 0;

	public static final int WARNING_SERVERITY = 1;

	protected static int defaultServerity = 1;

	/**
	 * @param showStackDepth the showStackDepth to set
	 */
	public void setShowStackDepth(boolean showStackDepth) {
		this.showStackDepth = showStackDepth;
	}

	/**
	 * @return the stackDepth
	 */
	public int getStackDepth() {
		return stackDepth;
	}

	/**
	 * @param stackDepth the stackDepth to set
	 */
	public void setStackDepth(int stackDepth) {
		this.stackDepth = stackDepth;
	}

	/**
	 * @return the displayable
	 */
	public static boolean isDisplayable() {
		return displayable;
	}

	/**
	 * @param displayable the displayable to set
	 */
	public static void setDisplayable(boolean flag) {
		displayable = flag;
	}

}
